var cluster = require('cluster');
var os = require('os');

var express = require('express');
var zookeeper = require('node-zookeeper-client');
var httpProxy = require('http-proxy');

var CPUS = os.cpus().length;
var PORT = 80;
var CONNECTION_STRING = '127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183';
var REGISTRY_ROOT = '/registry';

if (cluster.isMaster) {
    for (var i = 0; i < CPUS; i++) {
        cluster.fork();
    }
    cluster.on('exit', function (worker, code, signal) {
        console.log('worker %d died', worker.process.pid);
    });
} else {
    console.log('I am worker %s', cluster.worker.id);
// 连接 ZooKeeper
    var zk = zookeeper.createClient(CONNECTION_STRING);
    zk.connect();

// 创建代理服务器对象并监听错误事件
    var proxy = httpProxy.createProxyServer();
    proxy.on('error', function (err, req, res) {
        res.end('error'); // 输出error
    });

// 启动 Web 服务器
    var app = express();
// 缓存
    var cache = {};
    app.all('*', function (req, res) {
        // 处理图标请求
        if (req.path == '/favicon.ico') {
            res.end();
            return;
        }
        // 获取服务名称
        var serviceName = req.host.split('.')[0];
        console.log('serviceName: %s', serviceName);
        if (!serviceName) {
            console.log('serviceName is null');
            res.end();
            return;
        }

        var path = cache[serviceName + 'Path'];

        if (typeof(path) == "undefined") {
            path = '/-';
        }

        zk.exists(path, function (error, stat) {
            if (stat) {
                //读取缓存
                var serviceAddress = cache[serviceName];
                console.log('read cache: %s', serviceAddress);
                // 执行反向代理
                proxy.web(req, res, {
                    target: 'http://' + serviceAddress // 目标地址
                });
            } else {
                cache = {};
                console.log('clean');
                // 获取服务路径
                var servicePath = REGISTRY_ROOT + '/' + serviceName;
                console.log('servicePath: %s', servicePath);
                // 获取服务路径下的地址节点
                zk.getChildren(servicePath, function (error, addressNodes) {
                    if (error) {
                        console.log(error.stack);
                        res.end();
                        return;
                    }
                    var size = addressNodes.length;
                    if (size == 0) {
                        console.log('address node is not exist');
                        res.end();
                        return;
                    }
                    // 生成地址路径
                    var addressPath = servicePath + '/';
                    if (size == 1) {
                        // 若只有一个地址，则获取该地址
                        addressPath += addressNodes[0];
                    } else {
                        // 若存在多个地址，则随机获取一个地址
                        addressPath += addressNodes[parseInt(Math.random() * size)]
                    }
                    console.log('addressPath: %s', addressPath);
                    cache[serviceName + 'Path'] = addressPath;
                    // 获取服务地址
                    zk.getData(addressPath, function (error, serviceAddress) {
                        if (error) {
                            console.log(error.stack);
                            res.end();
                            return;
                        }
                        console.log('serviceAddress: %s', serviceAddress);
                        if (!serviceAddress) {
                            console.log('service address is not exist');
                            res.end();
                            return;
                        } else {
                            //写入缓存
                            console.log('%s write cache: %s', serviceName, serviceAddress);
                            cache[serviceName] = serviceAddress;
                            // 执行反向代理
                            proxy.web(req, res, {
                                target: 'http://' + serviceAddress // 目标地址
                            });
                        }
                    });
                });
            }
        });

    });
    app.listen(PORT, function () {
        console.log('server is running at %d', PORT);
    });

}